#include "fatUtiles.h"
#include "fatAddress.h"

/*============================================================================
getRootDirectory()
============================================================================*/
ST_LISTA_FILE_ENTRY * getRootDirectory(int iSocket){
	/*double		iCanctSectores;
	double			iCantClusters;
	ST_LISTA_FILE_ENTRY		*p=NULL;

	iCantSectores = (double)((bootSector.rootEntCnt*32) + (bootSector.bytesPorSector - 1) / bootSector.bytesPorSector);

	iCantClusters = (double) ( iCantSectores/bootSector.sectorPorCluster);
*/

//TODO: obtener cluster/s a pedir.
	return obtenerListaFileEntries(2, iSocket);
}

/*============================================================================
cargarBootSector()
Esta funcion le va a solicitar a la capa de direccionamiento el bloque 1,
y esa capa se encargará de separar los pedidos en n sectores.
============================================================================*/
void cargarBootSector() {
	ST_BLOQUE bloque;

	if (leerBloques(0, 1, bloque,stPFS.iSocketRemoto)){
		memcpy(&bootSector, &bloque, sizeof(ST_BOOTSECTOR));
		//printf("\tSectores por cluster: %u \n\tBytes por sector: %u\n\tnumFAT: %u\n",bootSector.sectorPorCluster,bootSector.bytesPorSector,bootSector.numberFATs);
	}

	return;
}

/*============================================================================
primerBloqueFAT()
Deuelve la posición del primer bloque de la FAT
============================================================================*/
uint32_t primerBloqueFAT(){
	return bootSector.reservedSectorCount * SECTOR_SIZE / BLOCK_SIZE;
}

/*============================================================================
cargarFatTable()
============================================================================*/
void cargarFatTable() {

	ST_BLOQUE bloque;
	uint32_t i;
	uint32_t primerBloque = primerBloqueFAT();

	// el total de clusters va a ser la cantidad de entradas de la FAT
	uint32_t totalClusters = bootSector.totalSectors32 / bootSector.sectorPorCluster;
	uint32_t entradasPorBloque = BLOCK_SIZE / ENTRY_SIZE;

	fatTable = (uint32_t *) malloc(totalClusters * ENTRY_SIZE);

	//printf("\ntotalClusters = %d\n", totalClusters);
	//printf("entradasPorBloque = %d\n\n", entradasPorBloque);

	// cantidad de bloques que ocupa la FAT
	uint32_t totalBloquesFat = totalClusters / entradasPorBloque;

	for (i = 0; i < totalBloquesFat; i++){
		leerBloques(primerBloque+i, 1, bloque, stPFS.iSocketRemoto);
		memcpy(fatTable+i*entradasPorBloque, &bloque, BLOCK_SIZE);
	}

	return;
}

/*============================================================================
escribirFat()
============================================================================*/
void escribirFat(uint32_t nroCluster, uint32_t valorCluster, int iSocket) {
	uint32_t entradasPorBloque = BLOCK_SIZE / ENTRY_SIZE;
	uint32_t primerBloque = primerBloqueFAT() + nroCluster / entradasPorBloque;

	fatTable[nroCluster] = valorCluster;

	ST_BLOQUE bloque;
	printf("primerBloque: %u nroCluster: %u entradasPorBloque: %u \n", primerBloque, nroCluster, entradasPorBloque);
	memcpy(&bloque, &fatTable[nroCluster - nroCluster % entradasPorBloque], BLOCK_SIZE);
	escribirBloques(primerBloque, 1, bloque, iSocket);
}

/*============================================================================
cantidadClustersLibres()
============================================================================*/
uint32_t cantidadClustersLibres(uint32_t totalClusters) {
	uint32_t i, clustersLibres = 0;

	sem_wait(&semaforoTablaFAT);
	for (i = 0; i < totalClusters; i++) {
		if (fatTable[i] == CLUSTER_LIBRE)
			++clustersLibres;
	}
	sem_post(&semaforoTablaFAT);

	return clustersLibres;
}

/*============================================================================
liberarListaEntries()
============================================================================*/
void liberarListaEntries(ST_LISTA_FILE_ENTRY *listaFileEntry) {
	 ST_LISTA_FILE_ENTRY 	*p;

	 while(listaFileEntry != NULL){
		p = listaFileEntry->next;
		free(listaFileEntry);
		listaFileEntry = p;
	}
}


/*============================================================================
liberarListaStrings()
============================================================================*/
void liberarListaStrings(char ** lista){
	int i = 0;

	while(lista[i] != NULL){
		free(lista[i]);
		i++;
	}

	free(lista);
}

/*============================================================================
getFileEntry()
Carga en file, la entrada correspondiente al path solicitado
============================================================================*/
int getFileEntry(const char *path, ST_FILE_ENTRY *file, int iSocket)
{
	ST_LISTA_FILE_ENTRY 	*listaFileEntry;
	ST_FILE_ENTRY 			fileEntry;
	ST_FILE_ENTRY 			*found;
	char 					entryName [14];

	if(strcmp(path,"/") == 0){
		memset(file, 0, sizeof(ST_FILE_ENTRY));
		file->tieneLongName = 0;
		file->dataEntry.attributes = 0x10;
		file->dataEntry.fileSize = 0;
		file->dataEntry.fstClusHi = 0;
		file->dataEntry.fstClusLo = 2;
		return 1;
	}

	listaFileEntry = getRootDirectory(iSocket); //solicitar cluster 2, trae toda la lista de entrada de directorios.

	char ** subpaths;	// esta lista va a tener los directorios incluidos en el path
	subpaths = strtok_modificada((char *)path, '/');
	int i = 1;

	while(subpaths[i] != NULL){
		//printf("subpath: %s\n",subpaths[i]);
		found = NULL;
		ST_LISTA_FILE_ENTRY * p = (ST_LISTA_FILE_ENTRY *)listaFileEntry;
		// recorro la lista de Entries, hasta encontrar la que estoy buscando
		while(p != NULL){
			getEntryName(&p->fileEntry, entryName);

			if(strcmp(entryName, subpaths[i]) == 0){
				found = &p->fileEntry;
				//printf("ENCONTRO EL DIRECTO: %s\n",entryName);
				break;
			}
			p=p->next;
		}

		// si no encontre la entry, devuelvo 0
		if (found == NULL){
			liberarListaEntries(listaFileEntry);
			liberarListaStrings(subpaths);
			return 0;
		}

		// si encontre la entry, pero no es un directorio, devuelvo 0
		if((found->dataEntry.attributes != 0x10) && (subpaths[i+1] != NULL)){
			liberarListaEntries(listaFileEntry);
			liberarListaStrings(subpaths);
			return 0;
		}

		memcpy(&fileEntry, found, sizeof(ST_FILE_ENTRY));
		liberarListaEntries(listaFileEntry);

		if(subpaths[i+1] == NULL){
			memcpy(file, &fileEntry, sizeof(ST_FILE_ENTRY));
			liberarListaStrings(subpaths);
			return 1;
		}else{
			listaFileEntry = obtenerListaDirectorio(&fileEntry, iSocket);
		}

		i++;
	}

	liberarListaStrings(subpaths);
	return 0;
}

/*============================================================================
getListaFileEntries()
============================================================================*/
ST_LISTA_FILE_ENTRY * obtenerListaFileEntries(uint32_t nroCluster, int iSocket) {

	ST_DIRECTORY	tempDirectory;
	ST_LISTA_FILE_ENTRY * directory = NULL;
	ST_LISTA_FILE_ENTRY * p;
	ST_LISTA_FILE_ENTRY * pAnt = NULL;
	ST_CLUSTER cluster;
	uint32_t inEntry = 0;
	uint32_t i;

	while (1){

//		leerCluster(nroCluster, cluster);
		accederCluster( nroCluster, cluster, LECTURA, iSocket );

		for(i = 0; i < sizeof(ST_CLUSTER); i += sizeof(ST_DIRECTORY)){

			memcpy(&tempDirectory, cluster+i, sizeof(ST_DIRECTORY));

			if (tempDirectory.name[0] == 0){ //La primera entrada despues de de la ultima entrada con datos empieza con 0, me avisa que se termino el listado.
				return directory;
			}

			if(tempDirectory.name[0] == 0xE5){
				continue;
			}

			if (!inEntry){
				p = (ST_LISTA_FILE_ENTRY *) malloc(sizeof(ST_LISTA_FILE_ENTRY));
				memset(&p->fileEntry, 0, sizeof(ST_FILE_ENTRY));
				inEntry = 1;
			}

			if (tempDirectory.attributes == 0x0F) {// Es entrada de nombre largo
				memcpy(&(p->fileEntry.longNameEntry), &tempDirectory, sizeof(ST_LONG_NAME));
				p->fileEntry.tieneLongName = 1;
			}else{//Es entrada de archivo
				memcpy(&(p->fileEntry.dataEntry), &tempDirectory, sizeof(ST_DIRECTORY));
				inEntry = 0;
				if(pAnt!=NULL){
					pAnt->next = p;
				}else{
					directory = p;
				}

				p->next = NULL;
				pAnt = p;
			}
		}

		sem_wait(&semaforoTablaFAT);
		nroCluster = fatTable[nroCluster];
		sem_post(&semaforoTablaFAT);
	}

	return directory;
}

/*============================================================================
getEntryName()
Dada una file entry, carga su name en buff, dependiendo de si es un dataEntry o una LongNameEntry
============================================================================*/
void getEntryName (ST_FILE_ENTRY * fileEntry, char * buff) {
	uint16_t longNameUTF16[14];
	char longName[14] = "";
	int i;

	if (fileEntry->tieneLongName == 1) {
		// primero copio los 10 bytes correspondientes a nameStart (uint16_t[5])
		memcpy(longNameUTF16, &fileEntry->longNameEntry.nameStart, 10);
		// luego de 5 posiciones, copio los 12 bytes correspondientes a nameMiddle (uint16_t[6])
		memcpy(longNameUTF16 + 5, &fileEntry->longNameEntry.nameMiddle, 12);
		// luego de 6 posiciones, copio los 4 bytes correspondientes a nameEnd (uint16_t[2])
		memcpy(longNameUTF16 + 11, &fileEntry->longNameEntry.nameEnd, 4);
		// seteo la ultima posicion en 0x0000
		longNameUTF16[13] = 0x0000;

		// busco un 0x0000 para saber la longitud del longNameUTF16
		for(i=0; i<14; i++){
			if(longNameUTF16[i] == 0x0000)
				break;
			//printf("obtiene el nombre desde el LNE\n");
		}

		//TODO: Descomentar las siguientes lineas y solucionar el error en la conversion de un UTF16 a un UTF8
		UErrorCode error = U_ZERO_ERROR;
		u_strToUTF8(longName, i+1, NULL, longNameUTF16, i, &error);

	}
	else {
		char name[9];
		char ext[4];

		// copio en name los 8 bytes correspondientes a name (uint8_t[8])
		strncpy(name, (char *)fileEntry->dataEntry.name, 8);
		name[8] = '\0';

		// copio en ext los 3 bytes correspondientes a extension (uint8_t[3])
		strncpy(ext, (char *)fileEntry->dataEntry.extension, 3);
		ext[3] = '\0';
		//printf("name y extension: %s - %s\n", name, ext);
		// cargo longname dependiendo de si tiene o no extension
		if (strlen(ext) > 0)
			sprintf(longName, "%s.%s", name, ext);
		else
			sprintf(longName, "%s", name);

		//printf("obtiene el nombre desde el data entrie\n");
	}

	strncpy(buff, longName, 14);
	return;
}

/*============================================================================
strtok_modificada()
Dado un delitador, separa la cadena en n partes y devuelve esa lista con n elementos.
============================================================================*/
char** strtok_modificada( char *str, char delimitor ){
	char **array;
	int elementsAmount = 0;

	{
		int cont;
		for(cont=0; str[cont] != '\0'; cont++ ){
			if( str[cont] == delimitor)	elementsAmount++;
		}
		elementsAmount++;
	}

	{
		array = calloc(elementsAmount+1, sizeof(char*) );
	}

	{
		int pibot, length, cont, elementIndex;
		for(cont=0, pibot=0, length=0, elementIndex=0; elementIndex != elementsAmount ; cont++ ){
			if( str[cont] == delimitor || str[cont] == '\0' ){
				array[elementIndex] = malloc(length + 1);
				if( length > 0 ){
					strncpy(array[elementIndex], &str[pibot], length);
					array[elementIndex][length] = '\0';
				} else {
					array[elementIndex][0] = '\0';
				}
				pibot = pibot + length + 1;
				length = 0;
				elementIndex++;
			} else {
				length++;
			}
		}
		array[elementIndex] = NULL;
	}
	return array;
}

/*============================================================================
dividirPath()
============================================================================*/
void dividirPath(const char * path, char * directory, char * file){
	int i=0;
	char * ultimaBarra = strrchr(path, '/');

	while(path+i != ultimaBarra) {
		directory[i] = path[i];
		i++;
	}

	if (i==0) {
		directory[i] = '/';
		i++;
	}

	directory[i] = '\0';
	strcpy(file, ultimaBarra+1);
}

/*============================================================================
obtenerListaDirectorio()
============================================================================*/
ST_LISTA_FILE_ENTRY * obtenerListaDirectorio(ST_FILE_ENTRY * fileEntry, int iSocket){

	if (fileEntry == NULL)
		return NULL;

	uint32_t nroCluster = obtenerPrimerCluster(&fileEntry->dataEntry);

	return obtenerListaFileEntries(nroCluster, iSocket);
}

/*============================================================================
obtenerPrimerCluster()
============================================================================*/
uint32_t obtenerPrimerCluster(ST_DIRECTORY * dataEntry) {
	uint32_t 	primerCluster;

	primerCluster = (uint32_t)(dataEntry->fstClusHi); // Deberia hacer que 0xAB sea 0x00AB
	primerCluster = primerCluster << 16; // Pongo los bits altos en la posicion que corresponden 0xAB00
	primerCluster = primerCluster | (uint32_t) (dataEntry->fstClusLo); //Convertimos el fstClusLo a uint32 y lo OR'eo bit a bit

	return primerCluster;
}

/*============================================================================
obtenerUltimoCluster()
============================================================================*/
uint32_t obtenerUltimoCluster(ST_FILE_ENTRY * fileEntry) {
	uint32_t nroCluster, temp;

	nroCluster = temp = obtenerPrimerCluster(&fileEntry->dataEntry);

	sem_wait(&semaforoTablaFAT);
	while((temp = fatTable[nroCluster]) != ULTIMO_CLUSTER) {
		nroCluster = temp;
	}
	sem_post(&semaforoTablaFAT);

	return nroCluster;
}

/*============================================================================
generaNombreCorto()
============================================================================*/
int generaNombreCorto(const char * nuevoNombre, ST_FILE_ENTRY * directorio, char * nombreCorto, int iSocket){
	ST_LISTA_FILE_ENTRY 	*p;
	int 					i;
	int						existeNombre = 0;

	if (strlen(nuevoNombre)<= 8){
		memset(nombreCorto,' ',8);
		memcpy(nombreCorto, nuevoNombre, strlen(nuevoNombre));
		nombreCorto[8]='\0';
		return 0;
	}else{
		memcpy(nombreCorto,nuevoNombre,6);
		nombreCorto[6]='˜';

		for (i=1;i<10;i++){
			nombreCorto[7]='0'+i;

			p = obtenerListaDirectorio(directorio, iSocket);

			while( p!=NULL ){
				if(memcmp(nombreCorto,p->fileEntry.dataEntry.name,8)==0){
					existeNombre = 1;
					break;
				}
				p=p->next;
			}

			if(  existeNombre == 0  )
				break;

		}
		assert( existeNombre == 0 );
		nombreCorto[8]='\0';
	}

//	for (i=0;i<strlen(nombreCorto);i++)
//		nombreCorto[i]=toupper(nombreCorto[i]);

	return 0;
}

/*============================================================================
checksum()
============================================================================*/
uint8_t checksum(const unsigned char *pFcbName){
	int i;
	unsigned char sum = 0;

	for (i=11; i; i--)
		sum = ((sum & 1) << 7) + (sum >> 1) + *pFcbName++;

	return sum;
}

/*============================================================================
renombrarFile()
============================================================================*/
int renombrarFile(const char * nuevoNombre, ST_FILE_ENTRY * destino, ST_FILE_ENTRY * fileEntry, int iSocket){
	char 		nombre[9];
	char 		nombreSinExt[15];
	char 		extension[4];
	char 		nombreCorto[12];
	uint16_t 	nombreLargo[14];
	int 		i = 0, j;

	if (fileEntry->dataEntry.attributes != 0x10){//no es un directorio
		//printf("renombrarFile :: voy a renombrar archivo\n");
		char * ulitmoPunto = strrchr(nuevoNombre,'.');
		while(nuevoNombre+i!=ulitmoPunto){
			nombreSinExt[i]=nuevoNombre[i];
			i++;
		}
		nombreSinExt[i]='\0';
		strcpy(extension,ulitmoPunto+1);

	}else{
		//printf("renombrarFile :: voy a renombrar directorio\n Nuevo nombre = %s\n", nuevoNombre);
		strncpy(nombreSinExt,nuevoNombre,8); //guardo los primeros 8 caracteres del directorio
		nombreSinExt[14]='\0';
		strncpy(extension,"   ",3);
		extension[3]='\0';
	}
	//printf("Nombre sin extension, antes de generarNombreCorto: %s\n",nombreSinExt);
	generaNombreCorto(nombreSinExt, destino, nombre, iSocket);
	//printf("despues de generar nombre corto:: %s\n",nombre);

	for (j = 0;j<strlen(extension);j++)
		extension[j]=toupper(extension[j]);

	for (j = 0;j<strlen(nombre);j++)
		nombre[j]=toupper(nombre[j]);

	memcpy(fileEntry->dataEntry.name, (uint8_t *)nombre, 8);
	memcpy(fileEntry->dataEntry.extension, (uint8_t *)extension, 3);

	//printf("DIRECTORY NAME = %s \n", (char *)fileEntry->dataEntry.name);

	UErrorCode error = U_ZERO_ERROR;
	u_strFromUTF8(nombreLargo, (strlen(nuevoNombre)+2) * 2, NULL, nuevoNombre, strlen(nuevoNombre)+1, &error);

	memset(nombreLargo+strlen(nuevoNombre)+1, 0, 14-(strlen(nuevoNombre)+1));//setea en 0 lo que no ocupa el nombre
	//printf("nombreLargo:: %x\n", nombreLargo);

	memcpy(fileEntry->longNameEntry.nameStart,nombreLargo,10);
	memcpy(fileEntry->longNameEntry.nameMiddle,&nombreLargo[5],12);
	memcpy(fileEntry->longNameEntry.nameEnd,&nombreLargo[11],4);

	strncpy(nombreCorto, nombre, 8);
	strncpy(nombreCorto+8, extension, 3);
	fileEntry->longNameEntry.checksum = checksum(nombreCorto);
	fileEntry->tieneLongName = 1;

	return 0;
}

/*============================================================================
crearDirectorio()
============================================================================*/
int crearDirectorio(const char * path, int iSocket){
	char parent[255], name[15];
	ST_FILE_ENTRY fileEntry, parentEntry;

	memset(&fileEntry, 0, sizeof(ST_FILE_ENTRY));

	dividirPath(path, parent, name);
	getFileEntry(parent, &parentEntry, iSocket);

	//printf("Atributo del padre (crearDirectorio):: %x\n", parentEntry.dataEntry.attributes);
	fileEntry.dataEntry.attributes = 0x10; //directorio
	fileEntry.dataEntry.fileSize = 0;

	renombrarFile(name, &parentEntry, &fileEntry, iSocket);

	fileEntry.longNameEntry.sequenceN = 0x41; //Primera y ultima (0x01+0x40);
	fileEntry.longNameEntry.attributes = 0x0F;
	fileEntry.dataEntry.fstClusLo = 0 & 0x0000FFFF;
	fileEntry.dataEntry.fstClusHi = 0 >> 16;

	agregarCluster(&fileEntry, iSocket);
	agregarEntrada(parent, fileEntry, iSocket);
	//printf("fileEntry.dataEntry.attributes: %x\n", fileEntry.dataEntry.attributes);
	//printf("name parent: %u\n", parentEntry.dataEntry.name);
	return 0; //no debe ser 0 porque dice en pantalla qe no se pudo crear el directorio
}

/*============================================================================
borrarDirectorio()
============================================================================*/
int borrarDirectorio(const char * path, int iSocket){
	ST_FILE_ENTRY fileEntry;
	getFileEntry(path, &fileEntry, iSocket);

	// chequear que sea directorio
	if (fileEntry.dataEntry.attributes != 0x10)
		return -1;

	truncarArchivo(path, 0, iSocket);

	fileEntry.dataEntry.name[0] = 0xE5;

	if(fileEntry.tieneLongName)
		fileEntry.longNameEntry.sequenceN = 0xE5;

	actualizarEntrada(path, &fileEntry, iSocket);
	return 0;
}

/*============================================================================
borrarArchivo()
============================================================================*/
int borrarArchivo(const char * path, int iSocket){
	ST_FILE_ENTRY fileEntry;
	getFileEntry(path, &fileEntry, iSocket);

	// chequear que sea archivo
	if (fileEntry.dataEntry.attributes != 0x20)
		return -1;

	truncarArchivo(path, 0, iSocket);

	fileEntry.dataEntry.name[0] = 0xE5;

	if(fileEntry.tieneLongName)
		fileEntry.longNameEntry.sequenceN = 0xE5;

	actualizarEntrada(path, &fileEntry, iSocket);
	return 0;
}

/*============================================================================
truncarArchivo()
============================================================================*/
int truncarArchivo(const char * path, off_t tamanio, int iSocket){
	ST_FILE_ENTRY fileEntry;
	uint32_t i, clustersFinales, clustersActuales;
	int32_t clustersDiff;	// negativo si tengo que achicar, positivo si tengo que agrandar
	//printf("truncar archivo - intento escribir en archivo: %s\n", path);
	getFileEntry(path, &fileEntry, iSocket);
	//printf("antes de cantidadClusters: %d\n", iSocket);
	clustersFinales = ceil((float) tamanio / CLUSTER_SIZE);
	clustersActuales = cantidadDeClusters(&fileEntry.dataEntry);
	//printf("despues de cantidadClusters: %d\n", iSocket);
	clustersDiff  = clustersFinales - clustersActuales;

	//printf("clustersFinales = %u \n clustersActuales = %u \n clustersDiff = %d \n", clustersFinales, clustersActuales, clustersDiff);
	//printf("ceil(5.5) = %f \n", ceil(5.5));

	if (clustersDiff < 0) {
		for (i = 0; i < abs(clustersDiff); i++){
			borrarUltimoCluster(&fileEntry, iSocket);
		}
	}
	else {
		for (i = 0; i < abs(clustersDiff); i++) {
			agregarCluster(&fileEntry, iSocket);
		}
	}

	fileEntry.dataEntry.fileSize = tamanio;

	actualizarEntrada(path, &fileEntry, iSocket);
	return 0;
}

/*============================================================================
agregarCluster()
============================================================================*/
int agregarCluster(ST_FILE_ENTRY * fileEntry, int iSocket){

	if(obtenerPrimerCluster(&fileEntry->dataEntry) != 0){
		uint32_t ultimoCluster = obtenerUltimoCluster(fileEntry);
		agregarClusterLibre(ultimoCluster, iSocket);
	}else{
		ST_CLUSTER cluster;

		memset(cluster, 0, CLUSTER_SIZE);
		uint32_t primerCluster = obtenerClusterLibre();

		//fatTable[primerCluster] = ULTIMO_CLUSTER;
		sem_wait(&semaforoTablaFAT);
		escribirFat(primerCluster, ULTIMO_CLUSTER, iSocket);
		sem_post(&semaforoTablaFAT);

		fileEntry->dataEntry.fstClusLo = primerCluster & 0x0000FFFF;
		fileEntry->dataEntry.fstClusHi = primerCluster >> 16;

		//printf("file->dataEntry.fstClusLo: %x \n", fileEntry->dataEntry.fstClusLo);
		//printf("file->dataEntry.fstClusHi: %x \n", fileEntry->dataEntry.fstClusHi);
		//imprimirFat(fileEntry->dataEntry.fstClusLo+2);

		accederCluster( primerCluster, cluster, ESCRITURA, iSocket );
	}

	return 0;
}

/*============================================================================
borrarUltimoCluster()
============================================================================*/
int borrarUltimoCluster(ST_FILE_ENTRY * fileEntry, int iSocket) {
	uint32_t primerCluster, ultimoCluster, penultimoCluster;

	primerCluster = obtenerPrimerCluster(&fileEntry->dataEntry);
	ultimoCluster = obtenerUltimoCluster(fileEntry);

	//printf("primerCluster = %u \n ultimoCluster = %u \n", primerCluster, ultimoCluster);

	if (ultimoCluster != primerCluster) {
		// en este caso, se busca el penultimo cluster para marcarlo como ultimo
		penultimoCluster = primerCluster;

		sem_wait(&semaforoTablaFAT);
		while((primerCluster = fatTable[primerCluster]) != ultimoCluster) {
			penultimoCluster = primerCluster;
		}
		sem_post(&semaforoTablaFAT);

		//fatTable[ultimoCluster] = CLUSTER_LIBRE;
		sem_wait(&semaforoTablaFAT);
		escribirFat(ultimoCluster, CLUSTER_LIBRE, iSocket);
		sem_post(&semaforoTablaFAT);
		//fatTable[penultimoCluster] = ULTIMO_CLUSTER;
		sem_wait(&semaforoTablaFAT);
		escribirFat(penultimoCluster, ULTIMO_CLUSTER, iSocket);
		sem_post(&semaforoTablaFAT);
	}else{
		// en este caso se borra el unico cluster que ocupaba la entrada

		//fatTable[ultimoCluster] = CLUSTER_LIBRE;
		sem_wait(&semaforoTablaFAT);
		escribirFat(ultimoCluster, CLUSTER_LIBRE, iSocket);
		sem_post(&semaforoTablaFAT);

		fileEntry->dataEntry.fstClusLo = 0 & 0x0000FFFF;
		fileEntry->dataEntry.fstClusHi = 0 >> 16;
	}

	//imprimirFat(ultimoCluster+2);

	return 0;
}

/*============================================================================
agregarClusterLibre()
============================================================================*/
int agregarClusterLibre(uint32_t ultimoCluster, int iSocket) {
	ST_CLUSTER cluster;
	memset(cluster, 0, CLUSTER_SIZE);

	uint32_t clusterLibre = obtenerClusterLibre();

	//fatTable[ultimoCluster] = clusterLibre;
	sem_wait(&semaforoTablaFAT);
	escribirFat(ultimoCluster, clusterLibre, iSocket);
	sem_post(&semaforoTablaFAT);
	//fatTable[clusterLibre] = ULTIMO_CLUSTER;
	sem_wait(&semaforoTablaFAT);
	escribirFat(clusterLibre, ULTIMO_CLUSTER, iSocket);
	sem_post(&semaforoTablaFAT);

	accederCluster( clusterLibre, cluster, ESCRITURA, iSocket );

	return 0;
}

/*============================================================================
obtenerProximoClusterLibre()
============================================================================*/
uint32_t obtenerClusterLibre() {
	uint32_t i, res = 0;

	sem_wait(&semaforoTablaFAT);
	for (i = 0; i < bootSector.totalSectors32 / bootSector.sectorPorCluster; i++) {
		if (fatTable[i] == CLUSTER_LIBRE) {
			res = i;
			break;
		}
	}
	sem_post(&semaforoTablaFAT);

	return res;
}

/*============================================================================
agregarEntrada()
============================================================================*/
int agregarEntrada(const char * directorio, ST_FILE_ENTRY fileEntry, int iSocket) {
	ST_FILE_ENTRY entradaDirectorio;
	ST_CLUSTER cluster;
	uint32_t primerCluster, primerEntradaLibre;

	getFileEntry(directorio, &entradaDirectorio, iSocket);
	primerCluster = obtenerPrimerCluster(&entradaDirectorio.dataEntry);
	//leerCluster(dataCluster, cluster);
	accederCluster( primerCluster, cluster, LECTURA, iSocket );

	while((primerEntradaLibre = encontrarEntradasLibres(&cluster, 2)) == -1){
		sem_wait(&semaforoTablaFAT);
		primerCluster = fatTable[primerCluster];
		sem_post(&semaforoTablaFAT);

		if (primerCluster == ULTIMO_CLUSTER){
			agregarCluster(&entradaDirectorio, iSocket);
			primerCluster = obtenerUltimoCluster(&entradaDirectorio);
			memset(&cluster, 0, CLUSTER_SIZE);
		}else{
			//leerCluster(dataCluster, cluster);
			accederCluster( primerCluster, cluster, LECTURA, iSocket );
		}
	}

	memcpy(&cluster[primerEntradaLibre], &fileEntry.longNameEntry, sizeof(ST_LONG_NAME));
	memcpy(&cluster[primerEntradaLibre + sizeof(ST_LONG_NAME)], &fileEntry.dataEntry, sizeof(ST_DIRECTORY));
	accederCluster( primerCluster, cluster, ESCRITURA, iSocket );

	return 0;
}

/*============================================================================
actualizarEntrada()
============================================================================*/
int actualizarEntrada(const char *path, ST_FILE_ENTRY * fileEntry, int iSocket) {
	uint32_t i, nroCluster;
	ST_FILE_ENTRY directorio, tempEntry;
	ST_DIRECTORY tempDataEntry;
	ST_CLUSTER cluster;

	int inEntry=0;
	uint32_t entryStart;

	char directoryPath[255];
	char fileName[15],anotherName[15];

	dividirPath(path, directoryPath, fileName);
	getFileEntry(directoryPath, &directorio, iSocket);

	nroCluster = obtenerPrimerCluster(&directorio.dataEntry);
	//leerCluster(nroCluster, cluster);
	accederCluster( nroCluster, cluster, LECTURA, iSocket );

	for(i = 0; i < CLUSTER_SIZE; i += sizeof(ST_DIRECTORY)){

		memcpy(&tempDataEntry, cluster+i, sizeof(ST_DIRECTORY));

		if (tempDataEntry.name[0]==0){
			return 0;
		}

		if(tempDataEntry.name[0]==0xE5){
			continue;
		}

		if (!inEntry){
			memset(&tempEntry, 0, sizeof(ST_FILE_ENTRY));
			entryStart = i;
			inEntry=1;
		}

		if (tempDataEntry.attributes == 0x0F) {// Es entrada de nombre largo
			memcpy(&(tempEntry.longNameEntry), &tempDataEntry, sizeof(ST_LONG_NAME));
			tempEntry.tieneLongName = 1;
			//printf("pase por un LNE \n", fileName, anotherName);
		}
		else {// Entrada de data
			memcpy(&(tempEntry.dataEntry), &tempDataEntry, sizeof(ST_DIRECTORY));
			getEntryName(&tempEntry, anotherName);

			//printf("fileName :: %s -- anotherName : %s \n", fileName, anotherName);

			if(strcmp(anotherName,fileName) == 0){
				memcpy(&cluster[entryStart], &fileEntry->longNameEntry, sizeof(ST_LONG_NAME));
				memcpy(&cluster[entryStart+sizeof(ST_LONG_NAME)], &fileEntry->dataEntry, sizeof(ST_LONG_NAME));
				break;
			}
			inEntry=0;
		}
	}

	//escribirCluster(nroCluster,cluster);
	accederCluster( nroCluster, cluster, ESCRITURA, iSocket );
	return 1;
}

/*============================================================================
encontrarEntradasLibres()
============================================================================*/
uint32_t encontrarEntradasLibres(ST_CLUSTER * cluster, size_t nroEntradas) {
	ST_DIRECTORY tempDataEntry;
	uint32_t i, primerEntradaLibre = 0, entradasLibres = 0, espacioLibre = 0;

	for(i = 0; i < CLUSTER_SIZE; i += sizeof(ST_DIRECTORY)) {
		memcpy(&tempDataEntry, *cluster+i, sizeof(ST_DIRECTORY));

		// Busco la primer entrada después de la última con datos
		if ( (tempDataEntry.name[0] == 0) || (tempDataEntry.name[0] == 0xE5)) {
			if(!espacioLibre) {
				espacioLibre = 1;
				primerEntradaLibre = i;
			}
			entradasLibres += 1;
		}else{
			espacioLibre = 0;
		}

		// Si ya encontró la cantidad de entradas buscadas, devuelvo la primer entrada libre
		if(entradasLibres == nroEntradas) {
			return primerEntradaLibre;
		}
	}

	return -1;
}

/*============================================================================
cantidadDeClusters()
============================================================================*/
uint32_t cantidadDeClusters(ST_DIRECTORY * dataEntry){
	uint32_t cnt = 1;
	uint32_t currentCluster = obtenerPrimerCluster(dataEntry);

	if(currentCluster == 0)
		return 0;

	//imprimirFat(20);
	sem_wait(&semaforoTablaFAT);
	while((currentCluster = fatTable[currentCluster]) != ULTIMO_CLUSTER) {
		cnt++;
	}
	sem_post(&semaforoTablaFAT);

	return cnt;
}

/*============================================================================
leerArchivo()
============================================================================*/
uint32_t leerArchivo( size_t size, off_t offset, char * buf, const char * path, int iSocket ) {
	char			szAuxiliar[100];
	uint32_t		res;
	ST_FILE_ENTRY	fileEntry;
	ST_CLUSTER 		cluster;

	uint32_t 		primerCluster;
	uint32_t 		i, offsetCluster, bytesALeer;
	uint32_t	 	posicionActual, leerDesde;
	uint32_t 		clustersALeer;
	uint32_t 		tamanioALeer;

	sprintf(szAuxiliar, "Lectura sobre el archivo: %s, de tamanio: %d", path, size);
	loguear( "pfsLog.txt", "PFS", "INFO", szAuxiliar);

	getFileEntry(path, &fileEntry, iSocket);

	primerCluster = obtenerPrimerCluster(&fileEntry.dataEntry);

	if(offset > fileEntry.dataEntry.fileSize)
		return 0;

	if ((offset + size) > fileEntry.dataEntry.fileSize)
		res = fileEntry.dataEntry.fileSize - offset;
	else
		res = size;

	clustersALeer = ceil(((float)offset+res)/CLUSTER_SIZE)-((float)offset/CLUSTER_SIZE);

	for (i = 0; i < offset/CLUSTER_SIZE; i++){
		sem_wait(&semaforoTablaFAT);
		primerCluster = fatTable[primerCluster];
		sem_post(&semaforoTablaFAT);

		if (primerCluster == ULTIMO_CLUSTER) {
			primerCluster = 0;
			break;
		}
	}

	bytesALeer = res;
	leerDesde = offset;
	posicionActual = 0;

	for (i = 0; i < clustersALeer; i++){

		//TODO: Buscar primero en cache
		//if (!leerDesdeCache(path, primerCluster, cluster))	// si no esta en cache
			accederCluster(primerCluster, cluster, LECTURA, iSocket);

		offsetCluster = leerDesde % CLUSTER_SIZE;

		if(bytesALeer > CLUSTER_SIZE - offsetCluster){
			tamanioALeer = CLUSTER_SIZE - offsetCluster;
		}else{
			tamanioALeer = bytesALeer;
		}

		memcpy(&buf[posicionActual], &cluster[offsetCluster], tamanioALeer);

		leerDesde += tamanioALeer;
		posicionActual += tamanioALeer;
		bytesALeer -= tamanioALeer;

		sem_wait(&semaforoTablaFAT);
		primerCluster = fatTable[primerCluster];
		sem_post(&semaforoTablaFAT);
	}

	return res;
}


/*============================================================================
escribirArchivo()
============================================================================*/
uint32_t escribirArchivo( size_t size, off_t offset, char * buf, const char * path, int iSocket ) {
	ST_CLUSTER cluster;
	uint32_t primerCluster, res;
	uint32_t i, offsetCluster, bytesAEscribir;
	uint32_t posicionActual, escribirDesde;
	uint32_t clustersAEscribir;
	uint32_t tamanioAEscribir;

	ST_FILE_ENTRY fileEntry;
	getFileEntry(path, &fileEntry, iSocket);

	if(offset +size > fileEntry.dataEntry.fileSize) {
		truncarArchivo(path, offset+size, iSocket);
		getFileEntry(path, &fileEntry, iSocket);
	}

	primerCluster = obtenerPrimerCluster(&fileEntry.dataEntry);
	res = size;
	clustersAEscribir = ceil((((float)offset+res)/CLUSTER_SIZE)-((float)offset/CLUSTER_SIZE));

	for (i = 0; i < offset/CLUSTER_SIZE; i++){
		sem_wait(&semaforoTablaFAT);
		primerCluster = fatTable[primerCluster];
		sem_post(&semaforoTablaFAT);

		if (primerCluster == ULTIMO_CLUSTER) {
			primerCluster = 0;
			break;
		}
	}

	bytesAEscribir = res;
	escribirDesde = offset;
	posicionActual = 0;

	for (i = 0; i < clustersAEscribir; i++) {

		accederCluster(primerCluster, cluster, LECTURA, iSocket);
		offsetCluster = escribirDesde % CLUSTER_SIZE;

		if(bytesAEscribir > CLUSTER_SIZE - offsetCluster)
			tamanioAEscribir = CLUSTER_SIZE - offsetCluster;
		else
			tamanioAEscribir = bytesAEscribir;

		memcpy(&cluster[offsetCluster], &buf[posicionActual], tamanioAEscribir);
		accederCluster(primerCluster, cluster, ESCRITURA, iSocket);

		escribirDesde += tamanioAEscribir;
		posicionActual += tamanioAEscribir;
		bytesAEscribir -= tamanioAEscribir;

		sem_wait(&semaforoTablaFAT);
		primerCluster = fatTable[primerCluster];
		sem_post(&semaforoTablaFAT);
	}

	return res;
}

/*============================================================================
imprimirClusters()
============================================================================*/
char * imprimirClusters(ST_FILE_ENTRY fileEntry) {
	char cadenaDeClusters [SECTOR_SIZE];// creo que con 512 alcanza (se muestran 20 clusters como maximo)
	memset(cadenaDeClusters, '\0', SECTOR_SIZE);

	uint32_t primerCluster = obtenerPrimerCluster(&fileEntry.dataEntry);
	uint32_t temp = primerCluster;
	uint32_t maximoClusters = 20;

	if (primerCluster == 0) {		
		//sprintf(cadenaDeClusters, "El archivo no tiene ningun cluster asociado.");
		strcpy(cadenaDeClusters, "El archivo no tiene ningun cluster asociado.");
		return cadenaDeClusters;
	}

	sprintf(cadenaDeClusters, "%u, ", temp);
	--maximoClusters;

	sem_wait(&semaforoTablaFAT);
	//printf("primerCluster: %u nextCluster: %u ULTIMO_CLUSTER %u \n", primerCluster, temp = fatTable[primerCluster], ULTIMO_CLUSTER);

	while((temp = fatTable[primerCluster]) != ULTIMO_CLUSTER && maximoClusters > 0) {
		sprintf(cadenaDeClusters, "%s%u, ", cadenaDeClusters, temp);
		primerCluster = temp;
		--maximoClusters;
	}
	sem_post(&semaforoTablaFAT);

	cadenaDeClusters[strlen(cadenaDeClusters)-2] = '\0';
	return cadenaDeClusters;
}
